﻿using System;
using System.ComponentModel;

namespace JeanFish.Blog
{
    /// <summary>
    /// The base class for models of blog.
    /// </summary>
    public abstract class ModelBase : IDisposable, IChangeTracking, INotifyPropertyChanged
    {
        /// <summary>
        /// All models use _id for the primary key.
        /// </summary>
        protected string _id = Guid.NewGuid().ToString("N");
        #region IDisposable
        private bool isDisposed = false;
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (!isDisposed)
            {
                if (disposing)
                {
                    //托管资源
                }
                //非托管资源
                isDisposed = true;
            }
        }
        ~ModelBase()
        {
            Dispose(false);
        }
        #endregion
        #region IChangeTracking
        /// <summary>
        /// Save all changes made on this model.
        /// </summary>
        public virtual void AcceptChanges()
        {
            if (!this.IsChanged) return;
            var result=this.Update();
            if (result == SavedResult.OK)
                this.IsChanged = false;
            OnSaved(result);
        }
        /// <summary>
        /// Indicate if the model has been changed.
        /// </summary>
        public bool IsChanged
        {
            get;
            private set;
        }
        #endregion
        #region INotifyPropertyChanged
        /// <summary>
        /// Set the value of model's properties.
        /// </summary>
        /// <typeparam name="TValueType">The type of the property.</typeparam>
        /// <param name="property">the name of the property.</param>
        /// <param name="newValue">the value will been assigned to the property.</param>
        /// <param name="oldValue">the value which the property has before.</param>
        /// <returns>If the assign operator has really changed the value of the property.</returns>
        protected virtual bool SetProperty<TValueType>(string property, TValueType newValue, ref TValueType oldValue)
        {
            bool isChanged = (!Object.Equals(newValue, oldValue));
            if (isChanged)
            {
                oldValue = newValue;
                this.IsChanged = isChanged;
                OnPropertyChanged(property);
            }
            return isChanged;
        }
        /// <summary>
        /// When the value of property changed.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        /// <summary>
        /// fire when the value of the property changes.
        /// </summary>
        /// <param name="property"></param>
        protected virtual void OnPropertyChanged(string property)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
        #endregion
        #region save
        /// <summary>
        /// Execute when AcceptChanges called performing the actual saving operation.
        /// </summary>
        /// <returns>The result of update operation.</returns>
        protected abstract SavedResult Update();
        /// <summary>
        /// When the model has been updated.
        /// </summary>
        public event EventHandler<SavedEventArgs> Saved;
        /// <summary>
        /// Fire when the model has been updated.
        /// </summary>
        /// <param name="result">the result of update operation.</param>
        private void OnSaved(SavedResult result)
        {
            if (this.Saved != null)
                Saved(this, new SavedEventArgs(this._id, result));
        }
        #endregion
    }
    /// <summary>
    /// The event argument for Saved event of ModelBase.
    /// </summary>
    public class SavedEventArgs : EventArgs
    {
        public SavedEventArgs(string id, SavedResult result)
        {
            this.ID=id;
            this.Result = result;
        }
        /// <summary>
        /// The id of the model.
        /// </summary>
        public string ID
        {
            get;
            private set;
        }
        /// <summary>
        /// the result of the updating operation.
        /// </summary>
        public SavedResult Result
        {
            get;
            private set;
        }
    }
    /// <summary>
    /// The save result.
    /// </summary>
    public enum SavedResult
    {
        /// <summary>
        /// Save successfully.
        /// </summary>
        OK=0,
        /// <summary>
        /// Save partially successfully.
        /// </summary>
        Partial=1,
        /// <summary>
        /// Save failed.
        /// </summary>
        Oops=2
    }
}